package com.suredy.core.service;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 通过对象操作的基础service
 * 
 * @author VIVID.G
 * @since 2015-4-1
 * @version v0.1
 */
public abstract class BaseSrvWithEntity<T> extends BaseSrv {

	private final static Logger log = LoggerFactory.getLogger(BaseSrvWithEntity.class);

	private Class<T> clazz;

	protected boolean USER_DEF_ORDER = true;

	protected final Map<String, Boolean> DEF_ORDER = new LinkedHashMap<String, Boolean>();

	@SuppressWarnings("unchecked")
	public BaseSrvWithEntity() {
		super();

		Class<?> clazz = this.getClass();

		while (clazz != Object.class) {
			Type t = clazz.getGenericSuperclass();

			if (!(t instanceof ParameterizedType))
				clazz = clazz.getSuperclass();

			Type[] ts = ((ParameterizedType) t).getActualTypeArguments();

			if (ts == null || ts.length <= 0)
				throw new RuntimeException("Can not get a default class.");

			if (ts[0] instanceof Class) {
				this.clazz = (Class<T>) ts[0];
				break;
			}
		}
	}

	public DetachedCriteria getDc(T t) {
		if (this.clazz == null)
			throw new RuntimeException("Can not get a default class type.");

		DetachedCriteria dc = null;

		if (t != null)
			dc = DetachedCriteria.forClass(t.getClass(), "T");
		else
			dc = DetachedCriteria.forClass(this.clazz, "T");

		return dc;
	}

	public Map<String, Boolean> addAnDefOrder(String entityField, boolean isAsc) {
		if (StringUtils.isBlank(entityField)) {
			log.warn("Invalid order info. The entity field is blank.");
			return this.DEF_ORDER;
		}

		if (this.DEF_ORDER.containsKey(entityField)) {
			log.warn("There are already a same field exists. Move it to the last and reset the value.");
			this.DEF_ORDER.remove(entityField);
		}

		this.DEF_ORDER.put(entityField, isAsc);

		return this.DEF_ORDER;
	}

	public T get(String id) {
		return super.get(this.clazz, id);
	}
	
	public T get(Long id) {
		return super.get(this.clazz, id);
	}
	
	public T get(Integer id) {
		return super.get(this.clazz, id);
	}

	public List<T> readByEntity(T t) {
		return this.readByEntity(t, null);
	}

	public List<T> readByEntity(T t, int page, int pageSize) {
		return (List<T>) this.readByEntity(t, page, pageSize, null);
	}

	public T readSingleByEntity(T t) {
		return (T) this.readSingleByEntity(t, null);
	}

	@SuppressWarnings("unchecked")
	public List<T> readByEntity(T t, LinkedHashMap<String, Boolean> order) {
		return (List<T>) this.readByCriteria(this.getDcForSearch(t, order));
	}

	@SuppressWarnings("unchecked")
	public List<T> readByEntity(T t, int page, int pageSize, LinkedHashMap<String, Boolean> order) {
		return (List<T>) this.readByCriteria(this.getDcForSearch(t, order), page, pageSize);
	}

	@SuppressWarnings("unchecked")
	public T readSingleByEntity(T t, LinkedHashMap<String, Boolean> order) {
		return (T) this.readSingleByCriteria(this.getDcForSearch(t, order));
	}

	public int getCountByEntity(T t) {
		return this.getCountByCriteria(this.getDcForSearch(t, null));
	}

	private DetachedCriteria getDcForSearch(T t, Map<String, Boolean> order) {
		DetachedCriteria dc = this.getDc(t);

		if (dc == null)
			throw new RuntimeException("Can not get a DetachedCriteria object.");

		if (order == null && this.USER_DEF_ORDER)
			order = this.DEF_ORDER;

		// 排序
		if (order != null)
			for (Map.Entry<String, Boolean> e : order.entrySet()) {
				String key = e.getKey();
				Boolean val = e.getValue();

				if (StringUtils.isBlank(key) || val == null) {
					log.warn("Invalid order key or value.");
					continue;
				}

				if (val)
					dc.addOrder(Order.asc(key));
				else
					dc.addOrder(Order.desc(key));
			}

		return dc;
	}

}
